package com.lxu;

/**
 * 懒汉式写法
 */
public class Singleton2 {
    /**
     * new Singleton2()本身不是原子性的，步骤如下：
     * 1、分配内存空间；
     * 2、初始化 Singleton2实例；
     * 3、返回Singleton2实例的内存地址给引用；
     * volatile的作用是 禁止 new Singleton2()的重排序
     *
     * 步骤2和步骤3是可能会重排序的，如果重排序了，可能会出现如下情况：
     * 线程A返回了Singleton2实例的内存地址，但此时Singleton2还没有初始化，此时线程B来获取Singleton2实例，
     * 发现确实存在了，就拿去用了，注意此时Singleton2并没有初始化，肯定就会出问题
     */
    private volatile static Singleton2 INSTANCE = null;

    private Singleton2() {
    }

    public static Singleton2 getInstance() {
        /**
         * 如果INSTANCE为null，就直接返回Singleton2对象，在多线程并发情况下，性能还是可以的
         */
        if (INSTANCE == null) {
            /**
             * 此处加锁是为了保证同时时间段只能进入一个线程，解决了多线程下的安全性问题；
             * 注意此处的synchronized不要加在方法上，多线程环境下，性能问题严重；
             */
            synchronized (Singleton2.class) {
                /**
                 * 为什么这地方还要判断一下呢？
                 * 假设不判断啊，很多线程同时来取对象，虽然synchronized加锁可以保证，同一时刻只能有一个线程执行这段方法，
                 * 但是上一个INSTANCE == null已经放进来了很多线程，A线程结束释放锁了，B线程进来了，还会new一次，所以
                 * 此时再判断一次，因此volatile保证了可见性，就直接返回了
                 */
                if (INSTANCE == null) {
                    return new Singleton2();
                }
            }
        }
        return INSTANCE;
    }
}
